﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NSAdapter;
using System.IO;

namespace CircuitMaker
{
    public partial class FrmCircuitGUI : Form
    {
        enum ActionState
        {
            noAction,
            addingGate,
            removingGate,
            addingConnection,
            removingConnection
        };

        enum PanelState
        {
            notWaiting,
            waitingForX1Y1,
            waitingForX2Y2
        };

        private Int32 DX;
        private Int32 DY;
        private Int32 GX;
        private Int32 GY;
        private Int32 NX;
        private Int32 NY;
        private Int32 R;

        private Int32 X1, Y1;
        private Int32 X2, Y2;

        ActionState currentAction;
        PanelState currentPanelState;
        String currentGateType;

        CircuitAdapter myCircuit;
        
        public FrmCircuitGUI()
        {
            InitializeComponent();

            DX = 10;    // horizontal grid spacing
            DY = 10;    // vertical grid spacing
            GX = 8;     // horizontal gate size
            GY = 6;     // vertical gate size
            NX = 80;    // horizontal number of grid cells
            NY = 60;    // vertical number of grid cells
            R = 7;      // radius of gate input/output

            currentAction = ActionState.noAction;
            currentPanelState = PanelState.notWaiting;
            currentGateType = "Low";
            myCircuit = new CircuitAdapter();
            UpdateMessage();
        }

        private void DrawGrid(Graphics graphics)
        {
            Pen pen = new Pen(Color.Black);
            for (int x = 0; x <= NX; x++)
                graphics.DrawLine(pen, x * DX, 0, x * DX, NY * DY);
            for (int y = 0; y <= NY; y++)
                graphics.DrawLine(pen, 0, y * DY, NX * DX, y * DY);
        }

        private void DrawGatesandConnections(Graphics graphics, String allInstructions)
        {
            foreach (string instructions in allInstructions.Split('\n'))
            {
                if (instructions.StartsWith("gate"))
                {
                    DrawGate(graphics, instructions);
                }
                else if (instructions.StartsWith("connection"))
                {
                    DrawConnection(graphics, instructions);
                }
            }
        }

        Brush getBrush(String value)
        {
            if (value == "L") return Brushes.Blue;
            if (value == "H") return Brushes.Red;
            return Brushes.Black;
        }
        
        void DrawGate(Graphics graphics, String instructions)
        {
            if (instructions == "") return;
            String[] parameter = instructions.Split(',');
            String nr = parameter[1];
            String type = parameter[2];
            Int32 Xposition   = Convert.ToInt32(parameter[3]);
            Int32 Yposition   = Convert.ToInt32(parameter[4]);
            Int32 NrOfInputs  = Convert.ToInt32(parameter[5]);
            String ValueInput1 = parameter[6];
            String ValueInput2 = parameter[7];
            String ValueOutput = parameter[8];

            Font font = new Font("Arial", 14);

            if (NrOfInputs == 2)
            {
                graphics.FillEllipse(getBrush(ValueInput1), Xposition * DX - R, (Yposition + GY/3) * DY - R, 2*R, 2*R);
                graphics.FillEllipse(getBrush(ValueInput2), Xposition * DX - R, (Yposition + 2*GY/3) * DY - R, 2*R, 2*R);
            } else
            if (NrOfInputs == 1)
            {
                graphics.FillEllipse(getBrush(ValueInput1), Xposition * DX - R, Yposition * DY + GY*DY/2 - R, 2*R, 2*R);
            }
            graphics.FillEllipse(getBrush(ValueOutput), (Xposition + GX) * DX - R, Yposition * DY + GY*DY / 2 - R, 2*R, 2*R);
            graphics.FillRectangle(Brushes.Yellow, Xposition * DX + 1, Yposition * DY + 1, GX * DX - 1, GY * DY - 1);
            graphics.DrawString(nr + " " + type, font, Brushes.Black, new Point(Xposition * DX, Yposition * DY));
        }

        void DrawConnection(Graphics graphics, String instruction)
        {
            String[] parameter = instruction.Split(',');
            Pen pen = new Pen(getBrush(parameter[1]), 2.0F);
            Int32 X1 = Convert.ToInt32(parameter[2]);
            Int32 Y1 = Convert.ToInt32(parameter[3]);
            Int32 X2 = Convert.ToInt32(parameter[4]);
            Int32 Y2 = Convert.ToInt32(parameter[5]);
            Int32 X12 = (X1 + X2) / 2;
            
            graphics.DrawLine(pen, new Point(X1 *DX, Y1*DY), new Point(X12*DX, Y1*DY));
            graphics.DrawLine(pen, new Point(X12*DX, Y1*DY), new Point(X12*DX, Y2*DY));
            graphics.DrawLine(pen, new Point(X12*DX, Y2*DY), new Point(X2 *DX, Y2*DY));
        }

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            Graphics graphics = e.Graphics;

            String allInstructions = myCircuit.getDrawingInstruction();

            if (showGridBox.Checked)
            {
                DrawGrid(graphics);
            }

            DrawGatesandConnections(graphics, allInstructions);
        }

        private void UpdateMessage()
        {
            switch (currentAction)
            {
                case ActionState.noAction:
                    messageLbl.Text = "Choose Action";
                    break;
                case ActionState.addingGate:
                    messageLbl.Text = "Click to Add Gate";
                    break;
                case ActionState.removingGate:
                    messageLbl.Text = "Click to Remove Gate";
                    break;
                case ActionState.addingConnection:
                case ActionState.removingConnection:
                    if (currentPanelState == PanelState.waitingForX2Y2)
                    {
                        messageLbl.Text = "Click on Second Gate";
                    }
                    else
                    {
                        messageLbl.Text = "Click on First Gate";
                    }
                    break;
            }
        }

        private void panel1_MouseClick(object sender, MouseEventArgs e)
        {
            if (currentPanelState == PanelState.notWaiting) return;

            if (currentAction == ActionState.addingGate)
            {
                myCircuit.addGate(e.X/DX, e.Y/DY, currentGateType);
            }
            else if (currentAction == ActionState.removingGate)
            {
                myCircuit.removeGate(e.X / DX, e.Y / DY);
            }
            else if (currentAction == ActionState.addingConnection && currentPanelState == PanelState.waitingForX1Y1)
            {
                X1 = e.X;
                Y1 = e.Y;
                currentPanelState = PanelState.waitingForX2Y2;
            }
            else if (currentAction == ActionState.addingConnection && currentPanelState == PanelState.waitingForX2Y2)
            {
                X2 = e.X;
                Y2 = e.Y;
                myCircuit.addConnection(X1 / DX, Y1 / DY, X2 / DX, Y2 / DY);
                currentPanelState = PanelState.waitingForX1Y1;
            }
            else if (currentAction == ActionState.removingConnection && currentPanelState == PanelState.waitingForX1Y1)
            {
                X1 = e.X;
                Y1 = e.Y;
                currentPanelState = PanelState.waitingForX2Y2;
            }
            else if (currentAction == ActionState.removingConnection && currentPanelState == PanelState.waitingForX2Y2)
            {
                X2 = e.X;
                Y2 = e.Y;
                myCircuit.removeConnection(X1 / DX, Y1 / DY, X2 / DX, Y2 / DY);
                currentPanelState = PanelState.waitingForX1Y1;
            }
            Refresh();
            UpdateMessage();
        }

        private void NotBtn_Click(object sender, EventArgs e)
        {
            currentGateType = "Not";
        }

        private void LowBtn_Click(object sender, EventArgs e)
        {
            currentGateType = "Low";
        }

        private void HighBtn_Click(object sender, EventArgs e)
        {
            currentGateType = "High";
        }

        private void OrBtn_Click(object sender, EventArgs e)
        {
            currentGateType = "Or";
        }

        private void AndBtn_Click(object sender, EventArgs e)
        {
            currentGateType = "And";
        }

        private void NoActionBtn_Click(object sender, EventArgs e)
        {
            GateTypeBox.Enabled = false;
            currentAction = ActionState.noAction;
            currentPanelState = PanelState.notWaiting;
            UpdateMessage();
        }

        private void addGateBtn_Click(object sender, EventArgs e)
        {
            GateTypeBox.Enabled = true;
            currentAction = ActionState.addingGate;
            currentPanelState = PanelState.waitingForX1Y1;
            UpdateMessage();
        }

        private void removeGateBtn_Click(object sender, EventArgs e)
        {
            GateTypeBox.Enabled = false;
            currentAction = ActionState.removingGate;
            currentPanelState = PanelState.waitingForX1Y1;
            UpdateMessage();
        }

        private void addConnectionBtn_Click(object sender, EventArgs e)
        {
            GateTypeBox.Enabled = false;
            currentAction = ActionState.addingConnection;
            currentPanelState = PanelState.waitingForX1Y1;
            UpdateMessage();
        }

        private void removeConnectionBtn_Click(object sender, EventArgs e)
        {
            GateTypeBox.Enabled = false;
            currentAction = ActionState.removingConnection;
            currentPanelState = PanelState.waitingForX1Y1;
            UpdateMessage();
        }

        private void showGridBox_CheckedChanged(object sender, EventArgs e)
        {
            circuitPanel.Refresh();
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            myCircuit.clearCircuit();
            circuitPanel.Refresh();
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                using (StreamWriter sw = new StreamWriter(saveFileDialog1.FileName))
                {
                    String serializedCircuit = myCircuit.serialize();
                    String[] lines = serializedCircuit.Split('\n');
                    foreach (String line in lines)
                    {
                        sw.WriteLine(line);
                    }
                }
            }
        }

        private void closeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                using (StreamReader sr = new StreamReader(openFileDialog1.FileName))
                {
                    String serializedString = "";
                    String line = "";

                    while ((line = sr.ReadLine()) != null)
                    {
                        serializedString += line + '\n';
                    }
                    myCircuit.deserialize(serializedString);
                    Refresh();
                }
            }
        }
    }
}
